﻿from django.core.cache import cache
from django.utils.deprecation import MiddlewareMixin
from log.models import RequestRecord
from django.http import JsonResponse
from log.logger import logger
from threading import Thread
from user.models import User
import time


class MyMiddleWare(MiddlewareMixin):

    # 记录用户的请求
    @staticmethod
    def record_request(request):
        request_list = cache.get('request_list', [])
        # 当缓存中的请求数量大于100后就将它们批量写入数据库
        try:
            RequestRecord.objects.bulk_create(
                [RequestRecord.create_request_record(**data) for data in request_list])  # 平均耗时：0.3s左右
            cache.delete('request_list')
        except Exception as e:
            logger.error(f'Failed to insert data into the db...error:{e}')

    # 处理请求
    def process_request(self, request):

        start_time = time.time()
        request.start_time = start_time
        ip = request.META.get('REMOTE_ADDR')
        path = request.path
        method = request.method
        user_agent = request.META.get('HTTP_USER_AGENT')
        http_refer = request.META.get('HTTP_REFERER', '')
        os = request.META.get('OS', '')

        # 获取ip的位置信息
        country, province, city = RequestRecord.get_address(ip)
        request_list = cache.get('request_list', [])
        request_log = {
            'ip': ip,
            'path': path,
            'method': method,
            'user_agent': user_agent,
            'http_refer': http_refer,
            'country': country,
            'province': province,
            'city': city,
            'os': os
        }
        request_list.append(request_log)
        cache.set('request_list', request_list)
        # 并发去执行请求记录
        if len(request_list) > 100:
            Thread(target=self.record_request, args=(request,)).start()

        res = None
        # # 实现限制某个时间段内的访问次数(例:1分钟内只能访问200次)
        ip = request.META.get('REMOTE_ADDR')
        ip_list = cache.get(ip, [])
        print(ip_list)
        # 时间限制在1分钟 (比较最新的请求和最早的请求的时间差，如果大于60秒，则踢出最早的请求)
        # 如果ip列表的长度大于200，就处于封禁状态，就不能进行pop操作
        while ip_list and time.time() - ip_list[-1] > 60 and len(ip_list) < 200:
            ip_list.pop()

        # # 最新的请求添加到请求列表的头部，那么最早的请求就在列表的尾部
        ip_list.insert(0, time.time())
        # 将请求列表保存在缓存中
        if len(ip_list) <= 200:
            cache.set(ip, ip_list, timeout=60)

        elif len(ip_list) <= 300:
            cache.set(ip, ip_list, timeout=60 * 30)  # 封禁30分钟
            res = {
                'code': '400',
                'msg': '喝杯茶休息一下吧!'
            }
            logger.warning(
                f'{request.user}[{request.META.get("REMOTE_ADDR")}] was forbidden for 0.5 hours...')

        elif len(ip_list) <= 500:
            cache.set(ip, ip_list, timeout=60 * 60 * 2)  # 封禁两个小时
            res = {
                'code': '400',
                'msg': '看个电影再来吧!'
            }
            logger.warning(
                f'{request.user}[{request.META.get("REMOTE_ADDR")}] was forbidden for 2 hours...')

        elif len(ip_list) <= 800:
            cache.set(ip, ip_list, timeout=60 * 60 * 24)  # 封禁一天
            res = {
                'code': '400',
                'msg': '不早了，睡觉觉吧!'
            }
            logger.warning(
                f'{request.user}[{request.META.get("REMOTE_ADDR")}] was forbidden for 1 day...')

        else:
            cache.set(ip, ip_list)  # 永久封禁
            res = {
                'code': '400',
                'msg': '对不起,是我们缘分不够!'
            }
            logger.warning(
                f'{request.user}[{request.META.get("REMOTE_ADDR")}] was forbidden forever...')

        # 如果用户登录了就进行用户位置的更新
        try:  # 如果用户没有登录get_signed_cookie会获取失败，因此需要异常捕获
            username = request.get_signed_cookie('username', salt='os-easy')
            status_key = request.get_signed_cookie(username, salt='os-easy')
            loc_status_key = cache.get(username)
            if status_key == loc_status_key:
                user = User.get_user_by_username(username)
                if request.method == 'GET':
                    user.longitude = request.GET.get(
                        'longitude', user.longitude)
                    user.latitude = request.GET.get('latitude', user.latitude)
                elif request.method == 'POST':
                    user.longitude = request.POST.get(
                        'longitude', user.longitude)
                    user.latitude = request.POST.get('latitude', user.latitude)
                user.save()
                request.user = user
        except Exception as e:
            pass
        # logger.error(
        #     f'{request.user}[{request.META.get("REMOTE_ADDR")}] failed to update locaion.error:{e}')

        return JsonResponse(res) if res else None

    
    def process_response(self, request, response):
        end_time = time.time()
        logger.info(
            f'{request.user}[{request.META.get("REMOTE_ADDR")}];{request.path}[{request.method}];cost {end_time - request.start_time} s.')

        return response

    # #处理错误
    # def process_exception(self, request, exception):
    #     res = JsonResponse(
    #         {
    #             'code': '500',
    #             'msg': '抱歉,服务器错误!'
    #         }
    #     )
    #     # 打印错误日志
    #     logger.error(
    #         f'{request.user}[{request.META.get("REMOTE_ADDR")}];{request.path}[{request.method}];error:{exception}.')

    #     return res
